package day30.zqStream;

import java.io.*;

/**
 * Java标准流测试类
 * <p>
 * 该类用于演示和测试Java中的三种标准流：
 * 1. System.in - 标准输入流（默认连接到键盘输入）
 * 2. System.out - 标准输出流（默认输出到控制台）
 * 3. System.err - 标准错误流（默认输出到控制台）
 * </p>
 * <p>
 * 标准流是程序与运行环境之间进行输入输出的预定义流对象，无需显式打开即可使用。
 * 它们在程序启动时自动打开，并且在程序执行期间始终处于打开状态。
 * </p>
 * <p>
 * 标准输出流(System.out)和标准错误流(System.err)虽然默认都输出到控制台，
 * 但它们是相互独立的，可以被单独重定向到不同的目标。
 * </p>
 * 
 * @author briup
 * @version 1.0
 * @see System
 * @see PrintStream
 * @see IOException
 */
public class testStandardStreamsSimple {
    /**
     * 程序入口点，用于执行标准流的各种测试
     * <p>
     * 该方法按顺序执行以下测试：
     * 1. 标准输出流测试 - 演示System.out的各种输出方式
     * 2. 标准错误流测试 - 演示System.err与System.out的区别
     * 3. 流重定向测试 - 演示如何将标准流重定向到文件
     * </p>
     * 
     * @param args 命令行参数数组
     */
    public static void main(String[] args) {
        // 打印测试开始标识信息
        System.out.println("=== Java标准流测试 ===\n");
        
        // 1. 调用标准输出流测试方法
        testStandardOutput();
        
        // 2. 调用标准错误流测试方法
        testStandardError();
        
        // 3. 调用流重定向测试方法
        testStreamRedirection();
        
        // 打印测试完成标识信息
        System.out.println("\n=== 测试完成 ===");
    }
    
    /**
     * 测试标准输出流 (System.out)的功能
     * <p>
     * System.out是PrintStream类的一个实例，提供了多种输出方法：
     * - println() - 输出后换行
     * - print() - 输出后不换行
     * - printf() - 格式化输出
     * </p>
     * <p>
     * 这些方法可以输出各种数据类型，包括基本数据类型和对象引用。
     * 当输出对象引用时，会自动调用该对象的toString()方法。
     * </p>
     */
    private static void testStandardOutput() {
        // 打印当前测试项标识
        System.out.println("1. 标准输出流测试:");
        
        // 基本输出方法测试
        // println()方法输出文本后自动换行
        System.out.println("  使用println输出一行文本");
        // print()方法输出文本后不换行，后续输出会连接在同一行
        System.out.print("  使用print输出不换行文本 ");
        System.out.println("这一部分在同一行");
        
        // 格式化输出测试
        // printf()方法支持格式化输出，使用C语言风格的格式说明符
        // %s表示字符串，%d表示十进制整数，\n表示换行符
        System.out.printf("  使用printf格式化输出: %s今年%d岁\n", "张三", 25);
        
        // 不同数据类型的输出测试
        // 通过字符串连接(+)操作符可以将不同数据类型转换为字符串输出
        System.out.println("  输出整数: " + 42);
        System.out.println("  输出浮点数: " + 3.14159);
        System.out.println("  输出布尔值: " + true);
        System.out.println("  输出字符: " + 'A');
        // 输出空行，用于分隔不同测试项
        System.out.println();
    }
    
    /**
     * 测试标准错误流 (System.err)的功能和与标准输出流的区别
     * <p>
     * System.err同样是PrintStream类的实例，主要用于输出错误信息。
     * 虽然默认情况下System.out和System.err都输出到控制台，但它们是独立的流，
     * 可以被分别重定向到不同的目标，例如将正常输出和错误信息分别写入不同的文件。
     * </p>
     * <p>
     * 在多线程环境中，System.err的输出通常是立即刷新的，而System.out可能被缓冲。
     * </p>
     */
    private static void testStandardError() {
        // 打印当前测试项标识
        System.out.println("2. 标准错误流测试:");
        
        // 正常信息输出到标准输出流
        // System.out通常用于输出程序的正常执行结果和状态信息
        System.out.println("  这是正常信息，输出到System.out");
        
        // 错误信息输出到标准错误流
        // System.err专门用于输出错误信息、警告和诊断信息
        System.err.println("  这是错误信息，输出到System.err");
        
        // 演示两者的区别 - 交替输出以观察效果
        System.out.println("  再次输出正常信息到System.out");
        System.err.println("  再次输出错误信息到System.err\n");
    }
    
    /**
     * 测试流重定向功能
     * <p>
     * Java允许在运行时重新分配标准流，将它们重定向到不同的目标。
     * 这通过System类的setIn()、setOut()和setErr()方法实现。
     * </p>
     * <p>
     * 流重定向的常见用途包括：
     * - 将输出保存到文件供后续分析
     * - 将错误信息重定向到专门的日志文件
     * - 实现程序的输入/输出过滤
     * </p>
     * <p>
     * 注意：在重定向流时，应该妥善管理资源，确保流在使用完毕后被正确关闭。
     * </p>
     */
    private static void testStreamRedirection() {
        // 打印当前测试项标识
        System.out.println("3. 流重定向测试:");
        
        try {
            // 保存原始的System.out引用，以便后续恢复
            // 这是一个重要的步骤，确保程序可以恢复到原始状态
            PrintStream originalOut = System.out;
            
            // 创建文件输出流，用于接收重定向的输出
            // FileOutputStream是字节流，用于向文件写入数据
            FileOutputStream fileOut = new FileOutputStream("output.txt");
            // 使用PrintStream包装FileOutputStream，提供格式化输出功能
            PrintStream printStream = new PrintStream(fileOut);
            
            // 重定向System.out到文件
            // 调用System.setOut()方法将标准输出重定向到指定的PrintStream
            System.setOut(printStream);
            // 以下输出将被写入到output.txt文件中，而不是控制台
            System.out.println("这条信息将被写入到output.txt文件中");
            System.out.println("这是第二条写入文件的信息");
            
            // 恢复原始的System.out
            // 将标准输出流恢复到控制台，后续输出将显示在控制台上
            System.setOut(originalOut);
            System.out.println("  流重定向测试完成，信息已写入output.txt文件");
            
            // 关闭文件流以释放系统资源
            // 应该先关闭PrintStream，再关闭底层的FileOutputStream
            printStream.close();
            fileOut.close();
            
            // 演示从文件读取刚刚写入的内容
            // 使用try-with-resources语句自动管理资源，确保流被正确关闭
            System.out.println("  从文件读取内容:");
            try (BufferedReader reader = new BufferedReader(new FileReader("output.txt"))) {
                String line;
                // 逐行读取文件内容并输出到控制台
                while ((line = reader.readLine()) != null) {
                    System.out.println("    " + line);
                }
            } catch (IOException e) {
                // 处理文件读取过程中可能出现的IO异常
                System.err.println("  读取文件时发生错误: " + e.getMessage());
            }
            
        } catch (IOException e) {
            // 处理流重定向过程中可能出现的IO异常
            System.err.println("  流重定向过程中发生错误: " + e.getMessage());
        }
    }
}